"""
Simple Async Task Queue
简单的异步任务队列 - 无需Redis，使用内存队列
"""

import asyncio
import uuid
from typing import Any, Callable, Dict, List, Optional
from datetime import datetime, timedelta
from enum import Enum
import structlog

logger = structlog.get_logger(__name__)


class TaskStatus(Enum):
    """任务状态"""

    PENDING = "pending"
    PROCESSING = "processing"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"


class Task:
    """任务对象"""

    def __init__(
        self,
        task_id: str,
        func: Callable,
        args: tuple = (),
        kwargs: dict = None,
        priority: int = 5,
        max_retries: int = 3,
        timeout: Optional[int] = None,
    ):
        self.task_id = task_id
        self.func = func
        self.args = args
        self.kwargs = kwargs or {}
        self.priority = priority
        self.max_retries = max_retries
        self.timeout = timeout

        self.status = TaskStatus.PENDING
        self.result: Any = None
        self.error: Optional[Exception] = None
        self.retry_count = 0
        self.created_at = datetime.now()
        self.started_at: Optional[datetime] = None
        self.completed_at: Optional[datetime] = None
        self.progress = 0
        self.progress_message = ""


class SimpleTaskQueue:
    """简单的内存任务队列"""

    def __init__(self, max_workers: int = 5, max_tasks: int = 1000):
        self.max_workers = max_workers
        self.max_tasks = max_tasks

        # 任务队列（按优先级排序）
        self.pending_tasks: List[Task] = []

        # 正在处理的任务
        self.processing_tasks: Dict[str, Task] = {}

        # 已完成/失败的任务
        self.completed_tasks: Dict[str, Task] = {}

        # 工作协程
        self.workers: List[asyncio.Task] = []
        self.running = False

        # 统计信息
        self.stats = {"total_processed": 0, "completed": 0, "failed": 0, "cancelled": 0}

    async def start(self):
        """启动任务队列"""
        if self.running:
            return

        logger.info("启动任务队列", max_workers=self.max_workers)
        self.running = True

        # 启动工作协程
        for i in range(self.max_workers):
            worker = asyncio.create_task(self._worker_loop(f"worker-{i}"))
            self.workers.append(worker)

    async def stop(self):
        """停止任务队列"""
        if not self.running:
            return

        logger.info("停止任务队列")
        self.running = False

        # 取消所有工作协程
        for worker in self.workers:
            worker.cancel()

        # 等待所有工作协程完成
        await asyncio.gather(*self.workers, return_exceptions=True)
        self.workers.clear()

    async def submit_task(
        self,
        func: Callable,
        args: tuple = (),
        kwargs: dict = None,
        priority: int = 5,
        max_retries: int = 3,
        timeout: Optional[int] = None,
    ) -> str:
        """
        提交任务

        Args:
            func: 要执行的函数
            args: 位置参数
            kwargs: 关键字参数
            priority: 优先级 (1-10, 数字越大优先级越高)
            max_retries: 最大重试次数
            timeout: 超时时间（秒）

        Returns:
            str: 任务ID
        """
        if len(self.pending_tasks) + len(self.processing_tasks) >= self.max_tasks:
            raise Exception(f"任务队列已满，最大任务数: {self.max_tasks}")

        task_id = str(uuid.uuid4())
        task = Task(
            task_id=task_id,
            func=func,
            args=args,
            kwargs=kwargs,
            priority=priority,
            max_retries=max_retries,
            timeout=timeout,
        )

        # 按优先级插入队列
        insert_index = 0
        for i, t in enumerate(self.pending_tasks):
            if t.priority <= task.priority:
                insert_index = i + 1
            else:
                break

        self.pending_tasks.insert(insert_index, task)

        logger.info(
            "任务已提交", task_id=task_id, func_name=func.__name__, priority=priority
        )

        return task_id

    async def get_task_status(self, task_id: str) -> Optional[Task]:
        """获取任务状态"""
        # 在待处理队列中查找
        for task in self.pending_tasks:
            if task.task_id == task_id:
                return task

        # 在处理中任务中查找
        if task_id in self.processing_tasks:
            return self.processing_tasks[task_id]

        # 在已完成任务中查找
        if task_id in self.completed_tasks:
            return self.completed_tasks[task_id]

        return None

    async def cancel_task(self, task_id: str) -> bool:
        """取消任务"""
        # 查找待处理任务
        for i, task in enumerate(self.pending_tasks):
            if task.task_id == task_id:
                task.status = TaskStatus.CANCELLED
                task.completed_at = datetime.now()
                self.pending_tasks.pop(i)
                self.completed_tasks[task_id] = task
                self.stats["cancelled"] += 1
                logger.info("任务已取消", task_id=task_id)
                return True

        # 查找处理中任务
        if task_id in self.processing_tasks:
            task = self.processing_tasks[task_id]
            task.status = TaskStatus.CANCELLED
            task.completed_at = datetime.now()
            # 注意：无法真正取消正在执行的协程，只能标记状态
            logger.info("任务取消请求已发送", task_id=task_id)
            return True

        return False

    async def _worker_loop(self, worker_id: str):
        """工作协程主循环"""
        logger.info("工作协程启动", worker_id=worker_id)

        while self.running:
            try:
                # 获取待处理任务
                task = await self._get_next_task()
                if task is None:
                    await asyncio.sleep(0.1)
                    continue

                # 处理任务
                await self._process_task(task)

            except asyncio.CancelledError:
                logger.info("工作协程被取消", worker_id=worker_id)
                break
            except Exception as e:
                logger.error("工作协程错误", worker_id=worker_id, error=str(e))
                await asyncio.sleep(1)

        logger.info("工作协程停止", worker_id=worker_id)

    async def _get_next_task(self) -> Optional[Task]:
        """获取下一个待处理任务"""
        if not self.pending_tasks:
            return None

        # 返回优先级最高的任务
        return self.pending_tasks.pop(0)

    async def _process_task(self, task: Task):
        """处理单个任务"""
        task_id = task.task_id

        try:
            # 移动任务到处理中
            task.status = TaskStatus.PROCESSING
            task.started_at = datetime.now()
            self.processing_tasks[task_id] = task

            logger.info(
                "开始处理任务",
                task_id=task_id,
                func_name=task.func.__name__,
                retry_count=task.retry_count,
            )

            # 执行任务
            if task.timeout:
                result = await asyncio.wait_for(
                    task.func(*task.args, **task.kwargs), timeout=task.timeout
                )
            else:
                result = await task.func(*task.args, **task.kwargs)

            # 任务完成
            task.status = TaskStatus.COMPLETED
            task.result = result
            task.progress = 100
            task.completed_at = datetime.now()

            self.completed_tasks[task_id] = task
            del self.processing_tasks[task_id]

            self.stats["completed"] += 1
            self.stats["total_processed"] += 1

            logger.info(
                "任务处理完成",
                task_id=task_id,
                duration=(task.completed_at - task.started_at).total_seconds(),
            )

        except asyncio.TimeoutError:
            await self._handle_task_error(task, "任务超时")

        except Exception as e:
            await self._handle_task_error(task, f"任务执行错误: {str(e)}", e)

    async def _handle_task_error(
        self, task: Task, error_msg: str, error: Optional[Exception] = None
    ):
        """处理任务错误"""
        task_id = task.task_id

        logger.error(
            error_msg,
            task_id=task_id,
            retry_count=task.retry_count,
            max_retries=task.max_retries,
        )

        # 检查是否可以重试
        if task.retry_count < task.max_retries:
            task.retry_count += 1
            task.status = TaskStatus.PENDING
            task.error = error or Exception(error_msg)

            # 重新加入队列（降低优先级）
            task.priority = max(1, task.priority - 1)

            insert_index = 0
            for i, t in enumerate(self.pending_tasks):
                if t.priority <= task.priority:
                    insert_index = i + 1
                else:
                    break

            self.pending_tasks.insert(insert_index, task)
            del self.processing_tasks[task_id]

            logger.info(
                "任务将重试",
                task_id=task_id,
                retry_count=task.retry_count,
                priority=task.priority,
            )

        else:
            # 任务失败
            task.status = TaskStatus.FAILED
            task.error = error or Exception(error_msg)
            task.completed_at = datetime.now()

            self.completed_tasks[task_id] = task
            del self.processing_tasks[task_id]

            self.stats["failed"] += 1
            self.stats["total_processed"] += 1

            logger.error(
                "任务处理失败（达到最大重试次数）",
                task_id=task_id,
                max_retries=task.max_retries,
            )

    def get_stats(self) -> dict:
        """获取队列统计信息"""
        return {
            "pending": len(self.pending_tasks),
            "processing": len(self.processing_tasks),
            "completed": len(self.completed_tasks),
            "stats": self.stats.copy(),
            "max_workers": self.max_workers,
            "max_tasks": self.max_tasks,
            "is_running": self.running,
        }

    def cleanup_completed_tasks(self, max_age_hours: int = 24):
        """清理旧的任务记录"""
        cutoff_time = datetime.now() - timedelta(hours=max_age_hours)

        old_task_ids = []
        for task_id, task in self.completed_tasks.items():
            if task.completed_at and task.completed_at < cutoff_time:
                old_task_ids.append(task_id)

        for task_id in old_task_ids:
            del self.completed_tasks[task_id]

        if old_task_ids:
            logger.info(
                "清理旧任务记录",
                cleaned_count=len(old_task_ids),
                max_age_hours=max_age_hours,
            )


# 全局任务队列实例
task_queue = SimpleTaskQueue(max_workers=5)

# 导出主要组件
__all__ = ["TaskStatus", "Task", "SimpleTaskQueue", "task_queue"]
